package com.hengshan.common;

import com.hengshan.common.enums.ReturnCode;
import com.hengshan.exception.SystemException;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 异常结果全局统一包装
 */
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {

    /**
     * 全局异常 运行异常
     */
    @ExceptionHandler(value = {Exception.class, RuntimeException.class})
    public ResultBody exceptionHandler(Exception e) {
        log.error(e.getMessage());
        return ResultBody.fail(ReturnCode.SYSTEM_ERROR, e.getMessage());
    }

    /**
     * 自定义SystemException异常
     */
    @ExceptionHandler(SystemException.class)
    public ResultBody systemExceptionHandler(SystemException e) {
        log.error(e.getMessage());
        return ResultBody.fail(e.getCode(), e.getMessage());
    }

    /**
     * 认证异常
     */
    @ExceptionHandler(BadCredentialsException.class)
    public ResultBody badCredentialsExceptionHandler(BadCredentialsException e) {
        log.error(e.getMessage());
        return ResultBody.fail(ReturnCode.LOGIN_ERROR, e.getMessage());
    }

    /**
     * 认证异常
     */
    @ExceptionHandler(InsufficientAuthenticationException.class)
    public ResultBody insufficientAuthenticationExceptionHandler(InsufficientAuthenticationException e) {
        log.error(e.getMessage());
        return ResultBody.fail(ReturnCode.AUTH_ERROR, e.getMessage());
    }

    /**
     * 认证异常,token过期
     */
    @ExceptionHandler(ExpiredJwtException.class)
    public ResultBody expiredJwtExceptionHandler(ExpiredJwtException e) {
        log.error(e.getMessage());
        return ResultBody.fail(ReturnCode.INVALID_TOKEN, e.getMessage());
    }

    /**
     * 参数异常,前端以json格式提交有效
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultBody methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        log.error(e.getMessage());
        List<String> msgList = e.getAllErrors().stream()
                .map(ObjectError::getDefaultMessage).filter(Objects::nonNull).collect(Collectors.toList());
        return ResultBody.fail(ReturnCode.REQUEST_ERROR, msgList.toString());
    }

    /**
     * 参数异常,仅对于表单提交有效，对于以json格式提交会失效
     */
    @ExceptionHandler(BindException.class)
    public ResultBody bindExceptionHandler(BindException e) {
        log.error(e.getMessage());
        List<String> msgList = e.getAllErrors().stream()
                .map(ObjectError::getDefaultMessage).filter(Objects::nonNull).collect(Collectors.toList());
        return ResultBody.fail(ReturnCode.REQUEST_ERROR, msgList.toString());
    }

    /**
     * 参数异常,处理@Validate @RequestParam的参数校验不通过
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ResultBody constraintViolationExceptionHandler(ConstraintViolationException e) {
        log.error(e.getMessage());
        List<String> msgList = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage).filter(Objects::nonNull).collect(Collectors.toList());
        return ResultBody.fail(ReturnCode.REQUEST_ERROR, msgList.toString());
    }

    /**
     * 参数异常,请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResultBody methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        log.error("请求参数类型不匹配'{}'", request.getRequestURI());
        return ResultBody.fail(ReturnCode.REQUEST_ERROR, String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
    }

    /**
     * 参数异常,请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public ResultBody missingPathVariableExceptionHandler(MissingPathVariableException e, HttpServletRequest request) {
        log.error("请求路径中缺少必需的变量'{}'", request.getRequestURI());
        return ResultBody.fail(ReturnCode.REQUEST_ERROR, String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }
}
